package com.huanghj.mqJsm.web;


import java.util.Collection;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

import net.spy.memcached.MemcachedClient;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;

import com.octo.captcha.Captcha;
import com.octo.captcha.service.CaptchaServiceException;
import com.octo.captcha.service.captchastore.CaptchaAndLocale;
import com.octo.captcha.service.captchastore.CaptchaStore;

public class MemcachedCaptchaStore implements CaptchaStore, InitializingBean {
	private static Logger logger = LoggerFactory
			.getLogger(MemcachedCaptchaStore.class);

	private MemcachedClient cache;

	/**
	 * 图片过期时间（秒）
	 */
	private int exp = 180;

	private static final String KEY_HEAD = "__JCAPTCHA__";

	private static final String KEYS_KEY = "__JCAPTCHA__KETS__";

	public void init() {
		Set<String> keys = new HashSet<String>(100);
		cache.set(KEYS_KEY, 3 * 24 * 60 * 60, keys);
		logger.info("Init jcaptcha keys");
	}

	@SuppressWarnings("unchecked")
	private Set<String> getAllKeys() {
		Set<String> keys = (Set<String>) cache.get(KEYS_KEY);
		if (keys == null) {
			init();
			keys = (Set<String>) cache.get(KEYS_KEY);
		}
		return keys;
	}

	private void refreshKeys(String id, boolean remove) {
		Set<String> keys = getAllKeys();
		if (!remove) {
			keys.add(id);
		} else {
			keys.remove(id);
		}
		cache.set(KEYS_KEY, 3 * 24 * 60 * 60, keys);
	}

	/**
	 * Check if a captcha is stored for this id
	 * 
	 * @return true if a captcha for this id is stored, false otherwise
	 */
	public boolean hasCaptcha(String id) {
		return cache.get(KEY_HEAD + id) != null;
	}

	/**
	 * Store the captcha with the provided id as key. The key is assumed to be
	 * unique, so if the same key is used twice to store a captcha, the store
	 * will return an exception
	 * 
	 * @param id
	 *            the key
	 * @param captcha
	 *            the captcha
	 * 
	 * @throws CaptchaServiceException
	 *             if the captcha already exists, or if an error occurs during
	 *             storing routine.
	 */
	public void storeCaptcha(String id, Captcha captcha)
			throws CaptchaServiceException {
		cache.add(KEY_HEAD + id, exp, new CaptchaAndLocale(captcha));
		refreshKeys(id, false);
		logger.debug("Store captcha");
	}

	/**
	 * Store the captcha with the provided id as key. The key is assumed to be
	 * unique, so if the same key is used twice to store a captcha, the store
	 * will return an exception
	 * 
	 * @param id
	 *            the key
	 * @param captcha
	 *            the captcha
	 * @param locale
	 *            the locale used that triggers the captcha generation
	 * @throws com.octo.captcha.service.CaptchaServiceException
	 *             if the captcha already exists, or if an error occurs during
	 *             storing routine.
	 */
	public void storeCaptcha(String id, Captcha captcha, Locale locale)
			throws CaptchaServiceException {
		cache.add(KEY_HEAD + id, exp, new CaptchaAndLocale(captcha, locale));
		refreshKeys(id, false);
		logger.debug("Store captcha");
	}

	/**
	 * Retrieve the captcha for this key from the store.
	 * 
	 * @return the captcha for this id
	 * 
	 * @throws CaptchaServiceException
	 *             if a captcha for this key is not found or if an error occurs
	 *             during retrieving routine.
	 */
	public Captcha getCaptcha(String id) throws CaptchaServiceException {
		logger.debug("Get captcha {}", id);
		Object captchaAndLocale = cache.get(KEY_HEAD + id);
		return captchaAndLocale != null ? ((CaptchaAndLocale) captchaAndLocale)
				.getCaptcha() : null;
	}

	/**
	 * Retrieve the locale for this key from the store.
	 * 
	 * @return the locale for this id, null if not found
	 * @throws com.octo.captcha.service.CaptchaServiceException
	 *             if an error occurs during retrieving routine.
	 */
	public Locale getLocale(String id) throws CaptchaServiceException {
		Object captchaAndLocale = cache.get(KEY_HEAD + id);
		return captchaAndLocale != null ? ((CaptchaAndLocale) captchaAndLocale)
				.getLocale() : null;
	}

	/**
	 * Remove the captcha with the provided id as key.
	 * 
	 * @param id
	 *            the key
	 * 
	 * @return true if found, false otherwise
	 * 
	 * @throws CaptchaServiceException
	 *             if an error occurs during remove routine
	 */
	public boolean removeCaptcha(String id) {
		logger.debug("Remove captcha {}", id);

		if (cache.get(KEY_HEAD + id) != null) {
			cache.delete(KEY_HEAD + id);
			refreshKeys(id, true);
			return true;
		}
		return false;
	}

	/**
	 * get the size of this store
	 */
	public int getSize() {
		return getAllKeys().size();
	}

	/**
	 * Return all the contained keys
	 */
	@SuppressWarnings("rawtypes")
	public Collection getKeys() {
		return getAllKeys();
	}

	/**
	 * Empty the store
	 */
	public void empty() {
		logger.debug("Empty captchas");
		Set<String> keys = getAllKeys();
		for (String id : keys) {
			cache.delete(KEY_HEAD + id);
		}
		keys.clear();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.octo.captcha.service.captchastore.CaptchaStore#initAndStart()
	 */
	public void initAndStart() {
		// Nothing to do with map implementations
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.octo.captcha.service.captchastore.CaptchaStore#shutdownAndClean()
	 */
	public void cleanAndShutdown() {
		empty();
	}

	/**
	 * @param exp
	 *            the exp to set
	 */
	public void setExp(int exp) {
		this.exp = exp;
	}

	public void setCache(MemcachedClient cache) {
		this.cache = cache;
	}

	public void afterPropertiesSet() throws Exception {
		init();
	}

}
